
jQuery.fn.extend({
	spreadsheet: function(settings) 
	{

		settings = jQuery.extend({
			urlGet: 		"documentation.html", 			//local url, if you want to get a sheet from a url
			urlSave: 		"save.html", 					//local url, for use only with the default save for sheet
			editable: 		true, 							//bool, Makes the jSheetControls_formula & jSheetControls_fx appear
			urlMenu: 		"menu.html", 					//local url, for the menu to the right of title
			newColumnWidth: 120, 							//int, the width of new columns or columns that have no width assigned
			title: 			null, 							//html, general title of the sheet group
			inlineMenu:		null, 							//html, menu for editing sheet
			buildSheet: 	false,							//bool, string, or object
															//bool true - build sheet inside of parent
															//bool false - use urlGet from local url
															//string  - '{number_of_cols}x{number_of_rows} (5x100)
															//object - table
			calcOff: 		false, 							//bool, turns calculationEngine off (no spreadsheet, just grid)
			log: 			false, 							//bool, turns some debugging logs on (jS.log('msg'))
			lockFormulas: 	false, 							//bool, turns the ability to edit any formula off
			parent: 		this, 							//object, sheet's parent, DON'T CHANGE
			colMargin: 		18, 							//int, the height and the width of all bar items, and new rows
			fnBefore: 		function() {}, 					//fn, fires just before jQuery.sheet loads
			fnAfter: 		function() {}, 			     	//fn, fires just after all sheets load
			fnSave: 		function() { jS.saveSheet(); }, //fn, default save function, more of a proof of concept
			fnOpen: 		function() { 					//fn, by default allows you to paste table html into a javascript prompt for you to see what it looks likes if you where to use sheet
				var t = prompt('Paste your table html here');
				if (t) {
					jS.openSheet(t);
				}
			},
			fnClose: 		function() {}, //fn, default clase function, more of a proof of concept
			joinedResizing: false, //bool, this joins the column/row with the resize bar
			boxModelCorrection: 2 //int, attempts to correct the differences found in heights and widths of different browsers, if you mess with this, get ready for the must upsetting and delacate js ever
		}, settings);

		jQuery.fn.spreadsheet.settings = jS.s = settings;
		jS.s.fnBefore();

		
		//We need to take the sheet out of the parent in order to get an accurate reading of it's height and width
		//jQuery(this).html(jS.s.loading);
		jQuery(this).html('');
		
		jS.s.width = jQuery(this).width();
		jS.s.height = jQuery(this).height();
		
		if (jS.s.log) 
		{
			jQuery(jS.s.parent).after('<textarea id="' + jS.id.log + '" />');
		} else {
			jS.log = function() {}; //save time in recursion
		}
		
		if (!jQuery.scrollTo) {
			jS.followMe = function() {};
		}
		
		jS.log('Startup');
		

		//Make functions upper and lower case compatible
		//for (var k in cE.fn) 
		//{
		//	var kLower = k.toLowerCase();
		//	if (kLower != k) {
		//		cE.fn[kLower] = cE.fn[k];
		//	}
		//}
		
		jQuery(window).resize(function() {
			jS.s.width = jQuery(jS.s.parent).width();
			jS.s.height = jQuery(jS.s.parent).height();
			jS.sheetSyncSize();
		});

		var obj;

		if (jS.s.buildSheet) 
		{
			//override urlGet, this has some effect on how the topbar is sized
			if (typeof(jS.s.buildSheet) == 'object') {
				obj = jS.s.buildSheet;
				alert("DDD");
			} else if (jS.s.buildSheet == true || jS.s.buildSheet == 'true') {
				obj = jQuery(jQuery(this).html());
			} else if (jS.s.buildSheet.match(/x/i)) {
				//obj = jS.controlFactory.sheet(jS.s.buildSheet);
			}
		}		
		jS.openSheet(obj);		
	}
});

var jS = jQuery.spreadsheet = 
{
	version: '1.01',
	i: 0,
	s: {},//s = settings object, used for shorthand, populated from jQuery.sheet

	evt: 
	{
		keyDownHandler: 
		{
			enterOnTextArea: function(e) 
			{
				if (!e.shiftKey) 
				{
					return jS.evt.cellClick(key.DOWN);
				} else {
					return true;
				}
			},
			enter: function(e) 
			{
				if (!jS.cellLast.isEdit && !e.ctrlKey) 
				{
					return jS.evt.cellClick();
				} else {
					return jS.evt.cellClick(key.DOWN);
				}
			},
			tab: function(e) 
			{
				if (e.shiftKey) 
				{
					return jS.evt.cellClick(key.LEFT);
				} else {
					return jS.evt.cellClick(key.RIGHT);
				}
			},
			textAreaKeyDown: function(e) 
			{
				switch (e.keyCode) 
				{
					case key.ENTER: 	return jS.evt.keyDownHandler.enterOnTextArea(e);
						break;
					case key.TAB: 		return jS.evt.keyDownHandler.tab(e);
						break;
				}
			},
			formulaKeyDown: function(e) 
			{
				switch (e.keyCode) {
					case key.ESCAPE: 	jS.evt.cellEditAbandon();					break;
					case key.TAB: 		return jS.evt.keyDownHandler.tab(e);		break;
					case key.ENTER: 	return jS.evt.keyDownHandler.enter(e);	break;
					case key.LEFT:
					case key.UP:
					case key.RIGHT:
					case key.DOWN:		return jS.evt.cellClick(e.keyCode);		break;
					default: 			jS.cellLast.isEdit = true;
				}
			}
		},
		formulaKeyDown: function(e, isTextArea) 
		{
			//Switch is much faster than if statements
			//I found that it's much easier to go from the origin key (up, down, left, right, tab, enter) and then detect if the ctrl key or shift keys are down.
			//It's just difficult to look at later on and it's probably faster overall
			return (isTextArea ? jS.evt.keyDownHandler.textAreaKeyDown(e) : jS.evt.keyDownHandler.formulaKeyDown(e));
		},
		cellEditDone: function(bsheetClearActive) 
		{
			switch (jS.cellLast.isEdit) 
			{
				case true:
					// Any changes to the input controls are stored back into the table, with a recalc.
					var td = jS.cellLast.td;
					var recalc = false;
					
					//Lets ensure that the cell being edited is actually active
					if (td && td.hasClass(jS.cl.cell)) 
					{ 
						//This should return either a val from textbox or formula, but if fails it tries once more from formula.
						var v = jS.cellTextArea(td, true);

						//inputFormula.value;
						var noEditFormula = false;
						var noEditNumber = false;
						var noEditNull = false;
						var editedFormulaToFormula = false;
						var editedFormulaToReg = false;
						var editedRegToFormula = false;
						var editedRegToReg = false;
						var editedToNull = false;
						var editedNumberToNumber = false;
						var editedNullToNumber = false;
						
						var tdFormula = td.attr('formula');
						var tdPrevVal = td.attr('prevVal');

						if (v) {
							if (v.charAt(0) == '=') 
							{ //This is now a formula
								if (v != tdFormula) 
								{ //Didn't have a formula before but now does
									editedFormulaToFormula = true;
									jS.log('edit, new formula, possibly had formula');
								} else if (tdFormula) { //Updated using inline edit
									noEditFormula = true;
									jS.log('no edit, has formula');
								} else {
									jS.log('no edit, has formula, unknown action');
								}
							} else if (tdFormula) 
							{ //Updated out of formula
								editedRegToFormula = true;
								jS.log('edit, new value, had formula');
							} else if (!isNaN(parseInt(v))) 
							{
								if ((v != tdPrevVal && v != jS.obj.formula().val()) || (td.text() != v)) {
									editedNumberToNumber = true;
									jS.log('edit, from number to number, possibly in function');
								} else {
									noEditNumber = true;
									jS.log('no edit, is a number');
								}
							} else { //Didn't have a formula before of after edit
								editedRegToReg = true;
								jS.log('possible edit from textarea, has value');
							}
						} else { //No length value
							if (td.html().length > 0 && tdFormula) 
							{
								editedFormulaToReg = true;
								jS.log('edit, null value from formula');
							} else if (td.html().length > 0 && tdFormula) 
							{
								editedToNull = true;
								jS.log('edit, null value from formula');
							
							} else {
								noEditNull = true;
								jS.log('no edit, null value');
							}
						}
						
						td.removeAttr('prevVal');
						var vHTML = jS.manageTextToHtml(v);
						if (noEditFormula) 
						{
							td.html(tdPrevVal);
						} else if (editedFormulaToFormula) 
						{
							recalc = true;
							td.attr('formula', v.replace(/\n/g, ' ')).html('');
						} else if (editedFormulaToReg) 
						{
							recalc = true;
							td.removeAttr('formula').html(vHTML);
						} else if (editedRegToFormula) 
						{
							recalc = true;
							td.removeAttr('formula').html(vHTML);
						} else if (editedRegToReg) 
						{
							td.html(vHTML);
						} else if (noEditNumber) 
						{
							td.html(vHTML); 
						} else if (noEditNull) 
						{
							td.html(vHTML);
						} else if (editedNumberToNumber) 
						{
							recalc = true;
							td.html(vHTML);
						} else if (editedToNull) 
						{
							recalc = true;
							td.removeAttr('formula').html('');
						}
						
						if (recalc) 
						{
							jS.calc(jS.i);
						}
						
						if (bsheetClearActive != false) 
						{
							// Treats null == true.
							jS.sheetClearActive();
						}
						
						jS.attrH.setHeight(jS.cellLast.row, 'cell');
						
						jS.obj.formula().focus().select();
						jS.cellLast.isEdit = false;
					}
					break;
				default:
					jS.attrH.setHeight(jS.cellLast.row, 'cell', false);
					jS.sheetClearActive();
			}
		},
		cellEditAbandon: function(skipCalc) 
		{
			jS.themeRoller.clearCell();
			jS.themeRoller.clearBar();
			if (!skipCalc) 
			{
				var v = jS.cellTextArea(jS.cellLast.td, true);
				if (v) 
				{
					jS.cellLast.td.html(jS.manageTextToHtml(v));
					jS.sheetClearActive();
					if (v.charAt(0) == '=') 
					{
						jS.calc(jS.i);
					}
				} else { //Even if the cell is blank, that doesn't mean it's not active
					jS.sheetClearActive();
					jS.calc(jS.i);
				}
			}
			
			jS.cellLast.td = jS.obj.sheet().find('td:first');
			jS.cellLast.row = jS.cellLast.col = 0;
			0;
			jS.rowLast = jS.colLast = -1;
			
			jS.fxUpdate('', true);

			return false;
		},
		cellClick: function(keyCode) 
		{ //invoces a click on next/prev cell
			var h = 0;
			var v = 0;
			switch (keyCode) 
			{
				case key.UP: 		v--; break;
				case key.DOWN: 		v++; break;
				case key.LEFT: 		h--; break;
				case key.RIGHT: 	h++; break;
			}
			jQuery(jS.getTd(jS.i, jS.cellLast.row + v, jS.cellLast.col + h)).click();
			
			return false;
		},
		cellOnMouseDown: function(e) 
		{
			if (e.altKey) 
			{
				jS.cellSetActiveMulti(e);
				jQuery(document).mouseup(function() 
				{
					jQuery(this).unbind('mouseup');
					var v = jS.obj.formula().val();
					jS.obj.formula().val(v + jS.getTdRange());
				});
			} else {
				return jS.cellSetActiveMulti(e);
			}			
		},
		cellOnClickLocked: function(e) 
		{
			if (!isNaN(e.target.cellIndex)) 
			{
				if (!jQuery(e.target).attr('formula')) 
				{
					jS.evt.cellOnClickManage(jQuery(e.target));
				}
			} else {
				jS.evt.cellEditAbandon();
				jS.obj.formula().focus().select();
			}
		},
		cellOnClickReg: function(e) 
		{
			if (!isNaN(e.target.cellIndex)) 
			{		
				jS.evt.cellOnClickManage(jQuery(e.target));
			} else { //this won't be a cell
				var clickable = jQuery(e.target).hasClass('clickable');
				if (!clickable) 
				{
					jS.obj.formula().focus().select();
				} else { //this is an inline control
					//jS.cellEditAbandon(true);
				}
			}
		},
		cellOnClickManage: function(td) 
		{
			if (!td.hasClass(jS.cl.cell)) 
			{ //initial click
				jS.cellEdit(td);
				jS.log('click cell');
			} else { //inline edit, 2nd click
				jS.cellLast.isEdit = jS.isSheetEdit = true;
				jS.cellTextArea(td, false, true);
				jS.themeRoller.cell(td);
				jS.log('click, textarea over table activated');
			}
			jS.followMe(td);
		},
		tabOnMouseDown: function(e) 
		{
			var i = jQuery(e.target).attr('i');
			
			if (i != '-1' && i != jS.i) 
			{
				jS.setActiveSheet(jQuery('#' + jS.id.tableControl + i), i); jS.calc(i);
			} else if (i != '-1' && jS.i == i) 
			{
				jS.sheetTab();
			} else {
				jS.addSheet();
			}
			return false;
		},
		resizeBar: function(e, o) 
		{
			//Resize Column & Row & Prototype functions are private under class jSheet		
			var target = jQuery(e.target);
			var resizeBar = {
				start: function(e) 
				{
					
					jS.log('start resize');
					//I never had any problems with the numbers not being ints but I used the parse method
					//to ensuev non-breakage
					o.offset = target.offset();
					o.tdPageXY = [o.offset.left, o.offset.top][o.xyDimension];
					o.startXY = [e.pageX, e.pageY][o.xyDimension];
					o.i = o.getIndex(target);
					o.srcBarSize = o.getSize(target);
					o.edgeDelta = o.startXY - (o.tdPageXY + o.srcBarSize);
					o.min = 10;
					
					if (jS.s.joinedResizing) 
					{
						o.resizeFn = function(size) 
						{
							o.setDesinationSize(size);
							o.setSize(target, size);
						};
					} else {
						o.resizeFn = function(size) 
						{
							o.setSize(target, size);
						};
					}
					
					//We start the drag sequence
					if (Math.abs(o.edgeDelta) <= o.min) 
					{
						//some ui enhancements, lets the user know he's resizing
						jQuery(e.target).parent().css('cursor', o.cursor);
						
						jQuery(document)
							.mousemove(resizeBar.drag)
							.mouseup(resizeBar.stop);
						
						return true; //is resizing
					} else {
						return false; //isn't resizing
					}
				},
				drag: function(e) 
				{
					var newSize = o.min;

					var v = o.srcBarSize + ([e.pageX, e.pageY][o.xyDimension] - o.startXY);
					if (v > 0) 
					{// A non-zero minimum size saves many headaches.
						newSize = Math.max(v, o.min);
					}

					o.resizeFn(newSize);
					return false;
				},
				stop: function(e) 
				{	
					o.setDesinationSize(o.getSize(target));
					
					jQuery(document)
						.unbind('mousemove')
						.unbind('mouseup');

					jS.obj.formula()
						.focus()
						.select();
					
					target.parent().css('cursor', 'pointer');
					
					jS.log('stop resizing');
				}
			};
			
			return resizeBar.start(e);
		},
		scrollBars: function(killTimer) 
		{
			var o = { //cut down on recursion, grabe them once
				pane: jS.obj.pane(), 
				barLeft: jS.obj.barLeftParent(), 
				barTop: jS.obj.barTopParent()
			};
			
			jS.obj.pane().scroll(function() 
			{
				o.barTop.scrollLeft(o.pane.scrollLeft());//2 lines of beautiful jQuery js
				o.barLeft.scrollTop(o.pane.scrollTop());
			});
		},
		barMouseDown: 
		{
			select: function(o, e, selectFn, resizeFn) 
			{
				var isResizing = jS.evt.resizeBar(e, resizeFn);
						
				if (!isResizing) 
				{
					selectFn(e.target);
					o
						.unbind('mouseover')
						.mouseover(function(e) 
						{
							selectFn(e.target, true);
						})
						.mouseup(function() 
						{
							o
								.unbind('mouseover')
								.unbind('mouseup');
						});
				}
				
				return false;
			},
			first: 0,
			last: 0,
			height: function(o) 
			{			
				var selectRow = function () {};
				
				o //let any user resize
					.unbind('mousedown')
					.mousedown(function(e) 
					{
						jS.evt.barMouseDown.first = jS.evt.barMouseDown.last = jS.rowLast = jS.getBarLeftIndex(e.target);
						jS.evt.barMouseDown.select(o, e, selectRow, jS.rowResizer);
						
						return false;
					});
				if (jS.s.editable) 
				{ //only let editable select
					selectRow = function(o, keepCurrent) 
					{
						if (!keepCurrent) 
						{ 
							jS.themeRoller.clearCell();
							jS.themeRoller.clearBar();
						}
						
						var i = jS.getBarLeftIndex(o);
						
						jS.rowLast = i; //keep track of last row for inserting new rows
						
						jS.evt.barMouseDown.last = (i > jS.evt.barMouseDown.last ? i : jS.evt.barMouseDown.last);
						
						jS.fxUpdate((jS.evt.barMouseDown.first + 1) + ':' + (jS.evt.barMouseDown.last + 1), true);
						
						jS.cellSetActiveMultiRow(jS.evt.barMouseDown.last);
					};
				}
			},
			width: function(o) 
			{
				var selectColumn = function() {};
				
				o //let any user resize
					.unbind('mousedown')
					.mousedown(function(e) 
					{
						jS.evt.barMouseDown.first = jS.evt.barMouseDown.last = jS.colLast = jS.getBarTopIndex(e.target);
						jS.evt.barMouseDown.select(o, e, selectColumn, jS.columnResizer);
						
						return false;
					});
				if (jS.s.editable) 
				{ //only let editable select
					selectColumn = function(o, keepCurrent) 
					{
						if (!keepCurrent) 
						{ 
							jS.themeRoller.clearCell();
							jS.themeRoller.clearBar();
						}
						var i = jS.getBarTopIndex(o);
						
						jS.colLast = i; //keep track of last column for inserting new columns
						
						jS.evt.barMouseDown.last = (i > jS.evt.barMouseDown.last ? i : jS.evt.barMouseDown.last);
						
						jS.fxUpdate(cE.columnLabelString(jS.evt.barMouseDown.first + 1) + ':' + cE.columnLabelString(jS.evt.barMouseDown.last + 1), true);
						
						jS.cellSetActiveMultiColumn(jS.evt.barMouseDown.last);
					};
				}
			}
		}
	},
	columnResizer: 
	{
		xyDimension: 0,
		getIndex: function(td) 
		{
			return jS.getBarTopIndex(td);
		},
		getSize: function(o) 
		{
			
			return jS.attrH.width(o, true);
		},
		setSize: function(o, v) 
		{
			o.width(v);
		},
		setDesinationSize: function(w) 
		{
			jS.sheetSyncSizeToDivs();
			
			jS.obj.sheet().find('col').eq(this.i)
				.width(w)
				.css('width', w)
				.attr('width', w);
			
			jS.obj.pane().scroll();
		},
		cursor: 'w-resize'
	},
	rowResizer: 
	{
		xyDimension: 1,
			getIndex: function(o) 
			{
				return jS.getBarLeftIndex(o);
			},
			getSize: function(o) 
			{
				return jS.attrH.height(o, true);
			},
			setSize: function(o, v) 
			{
				if (v) {
				o
					.height(v)
					.css('height', v)
					.attr('height', v);
				}
				return jS.attrH.height(o);
			},
			setDesinationSize: function() 
			{
				//Set the cell height
				jS.attrH.setHeight(this.i, 'bar', true);
				
				//Reset the bar height if the resized row don't match
				jS.attrH.setHeight(this.i, 'cell', false);
				
				jS.obj.pane().scroll();
			},
			cursor: 's-resize'
	},	
	cellEdit: function(td) 
	{
		//This finished up the edit of the last cell
		jS.evt.cellEditDone();
		var loc = jS.getTdLocation(td);
		
		//Show where we are to the user
		jS.fxUpdate(loc);
		
		var v = td.attr('formula');
		if (!v) 
		{
			v = jS.manageHtmlToText(td.html());
		}
		
		jS.obj.formula()
			.val(v)
			.focus()
			.select();
		jS.cellSetActive(td, loc);
	},
	cellSetActive: function(td, loc) 
	{
		jS.cellLast.td = td; //save the current cell/td
		jS.cellLast.row = jS.rowLast = loc[0];
		jS.cellLast.col = jS.colLast = loc[1];
		
		jS.themeRoller.cell(td); //themeroll the cell and bars
		jS.themeRoller.barLeft(jS.cellLast.row);
		jS.themeRoller.barTop(jS.cellLast.col);
		
		td.addClass(jS.cl.cell); //add classes
		jS.obj.barLeft().find('div').eq(jS.cellLast.row).addClass(jS.cl.barSelected);
		jS.obj.barTop().find('div').eq(jS.cellLast.col).addClass(jS.cl.barSelected);
	},
	colLast: -1,
	rowLast: -1,
	cellLast: {
		td: null,
		row: null,
		col: null,
		isEdit: false
	},
	context: {},
	calc: function(tableI, fuel) 
	{
		jS.log('Calculation Started');
		if (!jS.s.calcOff) {
			cE.calc(new jS.tableCellProvider(tableI), jS.context, fuel);
			jS.isSheetEdit = false;
		}
		jS.log('Calculation Ended');
	},		
	cellTextArea: function(td, returnVal, makeEdit, setVal) 
	{
		//Remove Textarea and transfer value.
		var v;
		if (td) 
		{
			if (!makeEdit) 
			{
				var textArea = td.find('textarea');
				var textAreaVal = textArea.val();
				if (textAreaVal || jS.obj.formula().attr('disabled')) 
				{
					jS.log('Textarea value used');
					v = textAreaVal;
					textArea.remove();
					//td
					//	.css('text-align', '')
					//	.css('vertical-align', '');
				} else {
					jS.log('Formula value used');
					v = jS.obj.formula().val();
				}
				jS.obj.formula().removeAttr('disabled');
			} else {
				if (setVal) 
				{
					v = setVal;
				} else {
					v = jS.obj.formula().val();
				}
				
				jS.obj.formula().attr('disabled', 'true');
				
				var textArea = jQuery('<textarea id="tempText" class="clickable" />');
				var h = jS.attrH.height(td);
				
				//There was an error in some browsers where they would mess this up.
				td.parent().height(h + jS.attrH.boxModelCorrection());
				//create text area.  Agian, strings are faster than DOM.
				textArea
					.height(h < 75 ? 75 : h)
					.val(v)
					.click(function(){
						return false;
					})
					.keydown(function(e) 
					{
						
						return jS.evt.formulaKeyDown(e, true);
					});
				
				//Se we can look at the past value after edit.
				if (td.attr('formula')) 
				{
					td.attr('prevVal', td.text()).removeAttr('formula');
				}
				//add it to cell
				td.html(textArea);
				//focus textarea
				textArea
					.focus()
					.select();
			}
			if (returnVal) 
			{
				return v;
			}
		}
	},
	refreshLabelsColumns: function()
	{
		var w = 0;
		jS.obj.barTop().find('div').each(function(i) 
		{
			jQuery(this).text(cE.columnLabelString(i+1));
			w += jQuery(this).width();
		});
		return w;
	},
	refreshLabelsRows: function()
	{
		jS.obj.barLeft().find('div').each(function(i) 
		{
			jQuery(this).text((i + 1));
		});
	},
	openSheet: function(o) {
		if (!jS.isDirty ? true : confirm("Are you sure you want to open a different sheet?  All unsaved changes will be lost.")) 
		{
			//jS.controlFactory.header();
			
			var fnAfter = function(i, l) 
			{
				
				if (i == (l - 1)) 
				{
					jS.i = 0;
					jS.setActiveSheet();
					//jS.themeRoller.resize();
					//for (var i = 0; i <= jS.sheetCount; i++) 
					//{
						//jS.calc(i);
					//}
				}
			};
			
			if (!o) 
			{
				
				//jQuery('<div />').load(jS.s.urlGet, function() 
				//{
				//	var sheets = jQuery(this).find('table');
				//	sheets.each(function(i) 
				//	{
						//jS.controlFactory.sheetUI(jQuery(this), i, function() 
						//{ 
						//	fnAfter(i, sheets.length);
						//}, true);
				//	});
				//});
			} else {
				//var sheets = jQuery('<div />').html(o).find('table');
				//sheets.each(function(i) 
				//{
					//jS.controlFactory.sheetUI(jQuery(this), i,  function() 
					//{ 
						fnAfter(0, 1);
					//}, false);
				//});
			}
			return true;
		} else {
			return false;
		}
	},
	saveSheet: function() 
	{
		var v = jS.sheetDecorateRemove(true);
		var s = jQuery('<div />').html(v).html();

		jQuery.ajax({
			url: jS.s.urlSave,
			type: 'POST',
			data: 's=' + s,
			dataType: 'html',
			success: function(data) 
			{
				jS.setDirty(false);
				alert('Success! - ' + data);
			}
		});
	},

	setActiveSheet: function(o, i) 
	{
		if (o) 
		{
			//o.show().siblings().hide();
			//jS.obj.tabContainer().find('.ui-state-highlight').removeClass('ui-state-highlight');
			//jS.i = i;
			//jS.obj.tab().parent().addClass('ui-state-highlight');
			
		} else {
			//alert("XX");
			i = 0;
			//jS.obj.tableControl().siblings().not('div').hide();
			//jS.obj.tabContainer().find('.ui-state-highlight').removeClass('ui-state-highlight');
			//jS.obj.tab().parent().addClass('ui-state-highlight');
		}

		//if (!jS.isRowHeightSync[i]) 
		//{ //this makes it only run once, no need to have it run every time a user changes a sheet
		//	jS.isRowHeightSync[i] = true;
		//	jS.obj.spreadsheet().find('tr').each(function(j) 
		//	{
		//		jS.attrH.setHeight(j, 'cell');
				/*
				fixes a wired bug with height in chrome and ie
				It seems that at some point during the sheet's initializtion the height for each
				row isn't yet clearly defined, this ensures that the heights for barLeft match 
				that of each row in the currently active sheet when a user uses a non strict doc type.
				*/
		//	});
		//}

		//jS.sheetSyncSize();
		//jS.replaceWithSafeImg(jS.obj.spreadsheet().find('img'));
	},
	
	getTdId: function(tableI, row, col) 
	{
		return tableI + '_R' +row+'C'+ col;
	},
	getTd: function(tableI, row, col) {
		return document.getElementById(jS.getTdId(tableI, row, col));
	},
	tableCell: function(tableI, row, col) 
	{
		this.tableBodyId = 'jSheet' + tableI;
		this.tableI = tableI;
		this.row = row;
		this.col = col;
		this.value = jS.EMPTY_VALUE;
		
		//this.prototype = new cE.cell();
	},
	EMPTY_VALUE: {},
	log: function(msg) {  //The log prints: {Current Time}, {Seconds from last log};{msg}
		jS.time.set();
		jS.obj.log().prepend(jS.time.get() + ', ' + jS.time.diff() + '; ' + msg + '<br />\n');
	},
	replaceWithSafeImg: function(o) 
	{  //ensures all pictures will load and keep their respective bar the same size.
		o.each(function() 
		{			
			var src = jQuery(this).attr('src');
			jQuery(this).replaceWith(jS.controlFactory.safeImg(src, jS.getTdLocation(jQuery(this).parent())[0]));
		});
	},
	isDirty:  false,
	setDirty: function(dirty) 
	{
		jS.isDirty = dirty; 
	},
	appendToFormula: function(v, o) {
		var formula = jS.obj.formula();
		if (formula.attr('disabled')) {
			formula = jS.cellLast.td.find('textarea');
		}
		
		var fV = formula.val();
		
		if (fV.charAt(0) != '=') {
			fV = '=' + fV;
		}
		
		formula.val(fV + v);
	}		
};


var key = {
	BACKSPACE: 			8,
	CAPS_LOCK: 			20,
	COMMA: 				188,
	CONTROL: 			17,
	DELETE: 			46,
	DOWN: 				40,
	END: 				35,
	ENTER: 				13,
	ESCAPE: 			27,
	HOME: 				36,
	INSERT: 			45,
	LEFT: 				37,
	NUMPAD_ADD: 		107,
	NUMPAD_DECIMAL: 	110,
	NUMPAD_DIVIDE: 		111,
	NUMPAD_ENTER: 		108,
	NUMPAD_MULTIPLY: 	106,
	NUMPAD_SUBTRACT: 	109,
	PAGE_DOWN: 			34,
	PAGE_UP: 			33,
	PERIOD: 			190,
	RIGHT: 				39,
	SHIFT: 				16,
	SPACE: 				32,
	TAB: 				9,
	UP: 				38
};
